package com.zhuoyue.biz.core.api.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.zhuoyue.biz.core.api.domain.*;
import com.zhuoyue.biz.core.api.dto.req.DistanceReportDTO;
import com.zhuoyue.biz.core.api.service.GatewayRedisService;
import com.zhuoyue.biz.core.api.service.GatewayService;
import com.zhuoyue.biz.core.api.service.JpushService;
import com.zhuoyue.biz.core.command.cmd.common.scene.SceneExecuteCmd;
import com.zhuoyue.common.constant.ExtrasCode;
import com.zhuoyue.common.constant.MessageCode;
import com.zhuoyue.common.exception.ErrorType;
import com.zhuoyue.common.exception.ZyServerException;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.text.StrSubstitutor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zhuoyue.biz.core.api.dao.GeofencingDataReportMapper;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;

/**
 * @author wjming
 * @ClassName: GeofencingDataReportService
 * @Description: 地理围栏数据上报服务
 * @Date 2021-07-06 15:51
 */
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class GeofencingDataReportService extends ServiceImpl<GeofencingDataReportMapper, GeofencingDataReport> {
    private final GeofencingService geofencingService;
    private final LocalSceneService sceneService;
    private final GatewayService gatewayService;
    private final GatewayRedisService gatewayRedisService;
    private final JpushService pushService;
    private final HomeService homeService;
    private final AppUserService userService;
    /**
     * 圈内
     */
    private static final String WITHIN_A_CIRCLE = "0";
    /**
     * 圈外
     */
    private static final String OUTSIDE_A_CIRCLE = "1";
    /**
     * 每天执行
     */
    private static final String EVERY_DAY = "0";
    /**
     * 场景数据帧模板
     */
    private static final String SCENE_CMD = "5000${gatewayMac}0209${sceneCode}";

    /**
     * 地理围栏数据上报执行
     *
     * @param reqDto
     */
    @Transactional(rollbackFor = Exception.class)
    public void deal(List<DistanceReportDTO> reqDto) {
        String week = LocalDateTime.now().getDayOfWeek().getValue() + "";
        for (DistanceReportDTO distanceReport : reqDto) {
            if (StringUtils.isEmpty(distanceReport.getGatewayId())) {
                continue;
            }
            List<Geofencing> geofencingsSet = geofencingService.list(Wrappers.<Geofencing>lambdaQuery()
                    .eq(Geofencing::getGatewayId, distanceReport.getGatewayId()));
            if (CollectionUtils.isEmpty(geofencingsSet)) {
                continue;
            }
            for (Geofencing geoSet : geofencingsSet) {
                //如果是圈内,就进行圈内判断
                if (!EVERY_DAY.equals(geoSet.getTime()) && !StringUtils.contains(geoSet.getTime(), week)) {
                    continue;
                }
                goOrLeaveHome(distanceReport, geoSet);
            }
        }

    }


    /**
     * 回家或离家判断
     */
    private void goOrLeaveHome(DistanceReportDTO distanceReport, Geofencing geoSet) {
        if (distanceReport.getDistance() < geoSet.getDistance() && WITHIN_A_CIRCLE.equals(geoSet.getType())) {
            withinDeal(distanceReport, geoSet);
            return;
        }
        //如果是圈外,圈外判断
        else if (distanceReport.getDistance() > geoSet.getDistance() && OUTSIDE_A_CIRCLE.equals(geoSet.getType())) {
            outsideDeal(distanceReport, geoSet);
            return;
        }
    }

    /**
     * 圈外操作
     *
     * @param distanceReport
     * @param geoSet
     */
    private void outsideDeal(DistanceReportDTO distanceReport, Geofencing geoSet) {
        int count = count(Wrappers.<GeofencingDataReport>lambdaQuery()
                .eq(GeofencingDataReport::getGatewayId, geoSet.getGatewayId())
                .eq(GeofencingDataReport::getDataType, true));
        if (count < 1) {
            if (geoSet.getEnable()) {
                //保存数据
                boolean save = save(new GeofencingDataReport(distanceReport.getGatewayId(), geoSet.getId(), true));
                if (!save) {
                    throw new ZyServerException(ErrorType.SAVE_ERROR);
                }
                setSceneCmd(geoSet.getSceneId(), geoSet.getGatewayId(), distanceReport.getHomeId(), distanceReport.getDistance() + "", distanceReport.getAppUserId(), "1");
            }

            //删除圈外数据
            remove(Wrappers.<GeofencingDataReport>lambdaQuery()
                    .eq(GeofencingDataReport::getGatewayId, distanceReport.getGatewayId())
                    .eq(GeofencingDataReport::getDataType, false));
        }
    }

    /**
     * 圈内操作
     */
    private void withinDeal(DistanceReportDTO distanceReport, Geofencing geoSet) {
        //如果在圈内
        int count = count(Wrappers.<GeofencingDataReport>lambdaQuery()
                .eq(GeofencingDataReport::getGatewayId, geoSet.getGatewayId())
                .eq(GeofencingDataReport::getDataType, false));
        if (count < 1) {
            if (geoSet.getEnable()) {
                //执行场景
                setSceneCmd(geoSet.getSceneId(), geoSet.getGatewayId(), distanceReport.getHomeId(), distanceReport.getDistance() + "", distanceReport.getAppUserId(), "0");
                //保存数据
                save(new GeofencingDataReport(distanceReport.getGatewayId(), geoSet.getId(), false));
            }

            //删除圈外数据
            remove(Wrappers.<GeofencingDataReport>lambdaQuery()
                    .eq(GeofencingDataReport::getGatewayId, distanceReport.getGatewayId())
                    .eq(GeofencingDataReport::getDataType, true));

        }
    }


    /**
     * 设置场景cmd
     *
     * @param sceneId
     * @param gatewayId
     */
    private void setSceneCmd(String sceneId, String gatewayId, String homeId, String distance, String userId, String type) {
        //执行场景
        LocalSceneDO sceneDo = sceneService.getById(sceneId);
        if (sceneDo == null) {
            throw new ZyServerException(ErrorType.Service_Fail.getCode(), "场景不存在");
        }
        GatewayDO gatewayDo = gatewayService.getById(gatewayId);
        SceneExecuteCmd sceneExecuteCmd = new SceneExecuteCmd().getSceneExecuteCmd(gatewayDo.getZigbeeMac(), sceneDo.getCode());
        Map<String, Object> map = JSONObject.parseObject(JSON.toJSONString(sceneExecuteCmd));
        cmdSend(map, SCENE_CMD, gatewayDo.getZigbeeMac(), homeId, distance, userId, sceneDo.getName(), type);
    }

    /**
     * 发送命令
     *
     * @param stringObjectMap
     * @param cmd
     * @param gatewayMac
     */
    private synchronized void cmdSend(Map<String, Object> stringObjectMap, String cmd, String gatewayMac, String homeId, String distance, String userId, String sceneName, String type) {
        String replaceCmd = new StrSubstitutor(stringObjectMap).replace(cmd);
        gatewayRedisService.writeDataToGateway(gatewayMac, replaceCmd);
        jpush(homeId, distance, userId, sceneName, type);

    }


    /**
     * 数据推送
     *
     * @param homeId
     * @param distance
     * @param userId
     * @param sceneName
     * @param type
     */
    private void jpush(String homeId, String distance, String userId, String sceneName, String type) {
        String homeName = homeService.getHomeNameById(homeId);
        AppUserDO user = userService.getById(userId);
        String message = "已经为您执行了场景";
        if (WITHIN_A_CIRCLE.equals(type)) {
            message = "您即将回到" + homeName + "，" + sceneName + "场景已自动执行，欢迎回家";
        } else if (OUTSIDE_A_CIRCLE.equals(type)) {
            message = "您已离开" + homeName + "，" + sceneName + "场景已自动执行，祝您出行平安";
        }
        System.out.println("message = " + message);
        try {
            pushService.send(MessageCode.JPUSH_TITLE, message,
                    MessageCode.ACCOUNT_TYPE, user.getSourceId(), ExtrasCode.GEOFENCING_DATA_REPORT,
                    homeId, user.getId() + "", user.getSourceType().toString(), user.getOrganizationCode());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

